﻿#include <Render\Render_Win32\Render_Win32_OpenGL\RENDER_WIN32_OPENGL.HPP>
#include <Sys\PROJECTIONMAKER.HPP>
#include <Sys\RENDERUNITREG.HPP>
#include <MEMSTORAGE.HPP>

using namespace CxxlMan;
using namespace Cxxl3D;


extern "C" CXXL_DLLEXPORT
cxxlObject * cxxlCDECL CxxlMan_ObjectFactory(const wchar_t *ClassID_Arg, void *Arg, ISpirit *spirit);


// 元件介面的識別碼
#define CRender_Win32_OpenGL_ClassID L"EA488B4E-BEB8-41cb-9B3D-8AACEF548BA5"



// 自動註冊和註銷 DatagramDisposal
class AutoRegDatagramDisposal
{
public:
  // Constructor
  AutoRegDatagramDisposal()
  {
    RenderUnit_Reg(UNITEXTNAME, IDatagramDisposal_Win32_OpenGL_Group);
  }

  // Destructor
  ~AutoRegDatagramDisposal()
  {
    cxxlCM_ElementDelGroup(IDatagramDisposal_Win32_OpenGL_Group);
  }

}o_AutoRegDatagramDisposal;



// 存放渲染 OpenGL 的 Datagram Disposal 物件
class RenderDatagramDisposalCenter:public IRenderDatagramDisposalCenterOpengl,
  public IDatagramDisposal // 為了獲得橋接能力
{
  // 這是 pool
  class DatagramDisposalTree:public cxxlAVL_Tree<IDatagramDisposal_OpenGL, UNICODE_String, true>
  {
    bool cxxlFASTCALL isLeftAt(UNICODE_String O_Key, UNICODE_String addKey) const
    {
      return O_Key <= addKey;
    }

    bool cxxlFASTCALL isEqual(UNICODE_String O_Key, UNICODE_String findKey)  const
    {
      return O_Key == findKey;
    } 

  public:
    DatagramDisposalTree(ISpirit *spirit = Spirit_Easy)  // Constructor
     :cxxlObject(spirit)
    {
    }

  }m_DatagramDisposalTree;

  int FVF; // 保存頂點目前設定狀態, 0 未設定 1 有設定

  // 指定頂點格式
  virtual void cxxlFASTCALL SetFVF(int Flags)
  {
    int Judge = FVF ^ Flags; // 過濾出不一樣的屬性
    FVF = Flags;

    if(Judge & FVF_XYZ)
      if(Flags & FVF_XYZ)
        glEnableClientState(GL_VERTEX_ARRAY);
      else
        glDisableClientState(GL_VERTEX_ARRAY);

    if(Judge & FVF_DIFFUSE)
      if(Flags & FVF_DIFFUSE)
        glEnableClientState(GL_COLOR_ARRAY);
      else
        glDisableClientState(GL_COLOR_ARRAY);

    if(Judge & FVF_NORMAL)
      if(Flags & FVF_NORMAL)
        glEnableClientState(GL_NORMAL_ARRAY);
      else
        glDisableClientState(GL_NORMAL_ARRAY);

    if(Judge & FVF_TEXTURE)
    {
      glClientActiveTexture(GL_TEXTURE0_ARB);
      if(Flags & FVF_TEXTURE)
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
      else
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    }

  }

  cxxlList<ICameraLoopReset> m_ICameraLoopReset_List;

  // 放入 ICameraLoopReset 
  virtual void AddICameraLoopReset(const CxxlMan::Smart_Ptr<ICameraLoopReset> &ICameraLoopReset_Arg)
  {
    m_ICameraLoopReset_List.Push_Back(ICameraLoopReset_Arg);
  }

  // 取得 ID 指定的 DD_OpenGL
  virtual CxxlMan::Smart_Ptr<IDatagramDisposal_OpenGL> cxxlFASTCALL GetIDatagramDisposal_OpenGL(CxxlMan::UNICODE_String ID)
  {
    Smart_Ptr<IDatagramDisposal_OpenGL> IDatagramDisposal_OpenGL_Ptr = m_DatagramDisposalTree.GetObj(ID); // 先嘗試由 pool 取得

    if(IDatagramDisposal_OpenGL_Ptr.isNULL())
    {
      IDatagramDisposal_OpenGL_Ptr = Smart_Cast<IDatagramDisposal_OpenGL>( cxxlCM_GetElement(ID, UNICODE_String(IDatagramDisposal_Win32_OpenGL_Group) ) );
      if(IDatagramDisposal_OpenGL_Ptr.isNULL() == false)
        m_DatagramDisposalTree.Add(IDatagramDisposal_OpenGL_Ptr, ID);  // 放入 pool 中
    } 

    return IDatagramDisposal_OpenGL_Ptr;
  }


public:
  // Constructor
  RenderDatagramDisposalCenter()
    :cxxlObject(Spirit_Easy)    
  {
    FVF = 0;
  }

  // Destructor
  virtual ~RenderDatagramDisposalCenter()
  {}

  void CallICameraLoopReset()
  {
    m_ICameraLoopReset_List.ResetPT(toHead);
    unsigned long Count = m_ICameraLoopReset_List.GetCount();
    Smart_Ptr<ICameraLoopReset> ICameraLoopReset_Ptr = m_ICameraLoopReset_List++;
    for(unsigned long i = 0; i < Count; ICameraLoopReset_Ptr = m_ICameraLoopReset_List++, ++i)
      ICameraLoopReset_Ptr->Reset();
  }

  // 執行渲染，若 IRenderDatagram_Arg 對應的 IDatagramDisposal 不存在回覆 false
  bool cxxlFASTCALL Exec(const Smart_Ptr<IRenderDatagram> &IRenderDatagram_Arg, const CSmart_Ptr<Matrix4x4> &ViewMatrix_Arg)
  {
    UNICODE_String RenderDatagramID = GetRenderDatagramID(IRenderDatagram_Arg);
    Smart_Ptr<IDatagramDisposal_OpenGL> IDatagramDisposal_OpenGL_Ptr = m_DatagramDisposalTree.GetObj(RenderDatagramID); // 先嘗試由 pool 取得

    if(IDatagramDisposal_OpenGL_Ptr.isNULL())
    {
      IDatagramDisposal_OpenGL_Ptr = Smart_Cast<IDatagramDisposal_OpenGL>( cxxlCM_GetElement(RenderDatagramID, UNICODE_String(IDatagramDisposal_Win32_OpenGL_Group) ) );
      if(IDatagramDisposal_OpenGL_Ptr.isNULL())
        return false;

      m_DatagramDisposalTree.Add(IDatagramDisposal_OpenGL_Ptr, RenderDatagramID);  // 放入 pool 中
    }    

    IDatagramDisposal_OpenGL_Ptr->Exec(this, IRenderDatagram_Arg, ViewMatrix_Arg);
    return true;
  }

};


// 建構出 OpenGL 環境
class OpenGLContext:public cxxlObject
{ 

  Smart_Ptr<WinHandle> WinHandle_Ptr;

  HGLRC hRC;              // 窗口著色描述表句柄
  HDC hDC;                // OpenGL渲染描述表句柄

  // 只是為了在建構時取得 WinHandle
  Smart_Ptr<WinHandle> Pick_WinHandle(const Smart_Ptr<IWindow_Win32> &IWindow_Win32_Arg)
  {
    if(IWindow_Win32_Arg.isNULL())
      return NULL;
    else 
      return IWindow_Win32_Arg->GetWinHandle();
  }

public:

  // Constructor
  OpenGLContext(const Smart_Ptr<IWindow_Win32> &IWindow_Win32_Arg)
    :cxxlObject(Spirit_Urgent),
     WinHandle_Ptr(Pick_WinHandle(IWindow_Win32_Arg),this)
  {

    hRC = NULL;
    hDC = NULL;

    if(IWindow_Win32_Arg.isNULL())
      return;


    Smart_Ptr<IDispModeWin32> IDispModeWin32_Ptr = IWindow_Win32_Arg->GetIDispModeWin32();       // 取得目前所用的顯示模式
    Smart_Ptr<IDisplayModeData> IDisplayModeData_Ptr = IDispModeWin32_Ptr->GetDisplayModeData(); // 取得目前指定的顯示模式資料

    // pfd 告訴窗口我們所希望的東東，即窗口使用的像素格式
    PIXELFORMATDESCRIPTOR pfd=
     {
       sizeof(PIXELFORMATDESCRIPTOR),            // 上述格式描述符的大小
       1,                                        // 版本號
       PFD_DRAW_TO_WINDOW |                      // 格式支持窗口
       PFD_SUPPORT_OPENGL |                      // 格式必須支持OpenGL
       PFD_DOUBLEBUFFER,                         // 必須支持雙緩沖
       PFD_TYPE_RGBA,                            // 申請 RGBA 格式
       IDisplayModeData_Ptr->GetColorBits(),     // 選定色彩深度
       0, 0, 0, 0, 0, 0,                         // 忽略的色彩位
       0,                                        // 無Alpha緩存
       0,                                        // 忽略Shift Bit
       0,                                        // 無累加緩存
       0, 0, 0, 0,                               // 忽略聚集位
       16,                                       // 16位 Z-緩存 (深度緩存)
       8,                                        // // 8 bits stencil buffer
       0,                                        // 無輔助緩存
       PFD_MAIN_PLANE,                           // 主繪圖層
       0,                                        // Reserved
       0, 0, 0                                   // 忽略層遮罩
     };


    HWND hWnd = WinHandle_Ptr->Get_hWnd();
    if(hWnd == NULL)
      return;

    if (!(hDC=GetDC(hWnd)))              // 取得設備描述表了麼?
      return;

    GLuint PixelFormat;            // 保存查找匹配的結果

    if(!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))        // Windows 找到相應的象素格式了嗎?
    {
      ReleaseDC(hWnd,hDC);
      hDC = NULL;
      return;
    }

    if(!SetPixelFormat(hDC,PixelFormat,&pfd))        // 能夠設置象素格式麼?
    {
      ReleaseDC(hWnd,hDC);
      hDC = NULL;
      return;
    }

    if (!(hRC=wglCreateContext(hDC)))          // 能否取得著色描述表?
    {
      ReleaseDC(hWnd,hDC);
      hDC = NULL;
      return;
    }

    wglMakeCurrent(hDC,hRC);            // 嘗試激活著色描述表

    if(glewInit () != GLEW_NO_ERROR)
    {
      wglDeleteContext(hRC);
      ReleaseDC(hWnd,hDC);
      hRC = NULL;
      hDC = NULL;
    }

  }

  // Destructor
  ~OpenGLContext()
  {
    if(hRC)
      if(!wglMakeCurrent(NULL,NULL))
        wglDeleteContext(hRC);


    if(hDC)
    {
      HWND hWnd = WinHandle_Ptr->Get_hWnd();
      if(hWnd && hDC)
        ReleaseDC(hWnd,hDC);
    }

  }

  bool isOK()
  {
    return hDC != NULL;
  }

  HDC cxxlFASTCALL Get_hDC()
  {
    return hDC;
  }  

  Smart_Ptr<WinHandle> cxxlFASTCALL GetWinHandle() const
  {
    return WinHandle_Ptr;
  }


};

class CRender_Win32_OpenGL:public IRender_Win32_OpenGL
{
  // 由使用端提供的初始化函數
  InitOpenglFunc m_pInitOpenglFunc;

  // 相連結的視窗
  P2Smart_Ptr<IWindow_Win32> IWindow_Win32_Ptr;

  // 負責管理 IDatagramDisposal
  Smart_Ptr<RenderDatagramDisposalCenter> m_RDDC_Ptr;

  // 存放建置 OpenGL 的相關資料
  Smart_Ptr<OpenGLContext> m_OpenGLContext_Ptr;



  // 用來比對看視窗有沒有變動大小
  //LONG  WinWidth;
  //LONG  WinHigh;

  OpenGLContext * cxxlFASTCALL NewOpenGLContext(const Smart_Ptr<IWindow_Win32> &IWindow_Win32_Arg)
  {
    OpenGLContext *p = new OpenGLContext(IWindow_Win32_Arg);
    if(p->isOK())
      return p;
    else
      return NULL;
  }


  // Window_Win32 有變動時自動呼叫通知
  bool cxxlFASTCALL Update()
  {    
    CriticalSecHandle AutoCSH(csPreserve);

    m_OpenGLContext_Ptr = NewOpenGLContext((IWindow_Win32*)IWindow_Win32_Ptr);

    return InitSetup();
  }

  // 保存永續儲存資料
  virtual bool cxxlFASTCALL Save(const Smart_Ptr<IcxxlStorage> &IcxxlStorage_Arg, const UNICODE_String &BackupName)
  {
    return IcxxlStorage_Arg->Save(Smart_Ptr<IRender>(this), BackupName);
  }

  // 取回永續儲存資料，並立即執行，若有錯誤原資料不變
  virtual bool cxxlFASTCALL Load(const CxxlMan::Smart_Ptr<CxxlMan::IcxxlStorage> &IcxxlStorage_Arg, const CxxlMan::UNICODE_String &BackupName)
  {
    Smart_Ptr<IcxxlStorage> tmpStorage_Ptr(new MemStorage);
    if(tmpStorage_Ptr->Save(Smart_Ptr<cxxlPreserve>(this), L"Tmp") == false)
      return false;

    if(IcxxlStorage_Arg->Load(Smart_Ptr<cxxlPreserve>(this), BackupName))
    {
      if(Reset())
        return true;

      tmpStorage_Ptr->Load(Smart_Ptr<cxxlPreserve>(this),L"Tmp");
      Reset();
    }
    
    return false;
  }


  bool cxxlFASTCALL InitSetup()
  {
    if(m_OpenGLContext_Ptr != NULL)
    {
      if(m_pInitOpenglFunc != NULL)
        return m_pInitOpenglFunc();

      return true;
    }

    return false;
  }


  virtual bool cxxlFASTCALL Init(InitOpenglFunc pInitOpenglFunc_Arg)
  {
    CriticalSecHandle AutoCSH(csPreserve);
    m_pInitOpenglFunc = pInitOpenglFunc_Arg;

    return InitSetup();
  }


  Smart_Ptr<IWindow_Win32> cxxlFASTCALL GetIWindowWin32()
  {
    CriticalSecHandle AutoCSH(csPreserve);

    return IWindow_Win32_Ptr;
  }

  virtual bool cxxlFASTCALL Reset()
  {
    Smart_Ptr<IWindow_Win32> IWindowWin32_Ptr = GetIWindowWin32();

    if(IWindowWin32_Ptr.isNULL())
      return false;

    return IWindowWin32_Ptr->Reset();
  }

  // 若回覆 false 表示有某個 IRenderDatagram 找不到對應的 IDatagramDisposal
  virtual bool cxxlFASTCALL Draw()
  {

      // 避免執行期間被更換
      csPreserve.Enter();
      Smart_Ptr<cxxlList<ICamera> > Camera_List = GetCameraList();
      Smart_Ptr<OpenGLContext> ActiveOpenGLContext_Ptr = m_OpenGLContext_Ptr;
      csPreserve.Leave();

      if(ActiveOpenGLContext_Ptr.isNULL() == false)
      {
        
        RECT rect; // Client 的範圍

        HWND hWnd = ActiveOpenGLContext_Ptr->GetWinHandle()->Get_hWnd();
        GetClientRect(hWnd,&rect);

        /*
        bool isWinResize; // 若有變動才須重新計算投影矩陣
        if(rect.right != WinWidth || rect.bottom != WinHigh)
        {
          WinWidth = rect.right;
          WinHigh = rect.bottom;
          isWinResize = true;
        }
        else
          isWinResize = false;
        */

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度緩存

        Camera_List->ResetPT(toHead);
        unsigned long Count = Camera_List->GetCount();
        unsigned long i = 0;
        Smart_Ptr<ICamera> Camera_Ptr = (*Camera_List)++;
        for(; i < Count; ++i, Camera_Ptr = (*Camera_List)++)
        {
          float Left, Top, Right, Bottom; 
          Camera_Ptr->GetViewport(Left, Top, Right, Bottom);          

          // 每個 ICamera 的 Viewport 在視窗中的實際範圍
          // OpenGL 是左下為 0,0
          long X = rect.right * Left;
          long Y = rect.bottom * (1 - Bottom);
          long Width = rect.right * Right - X;
          // long Height = rect.bottom * (1-Top) - Y;
          long Height = rect.bottom * (Bottom - Top);

          glViewport(X, Y, Width, Height); // Set Viewport

          //CSmart_Ptr<Matrix4x4> ViewMatrix_cPtr = Camera_Ptr->GetViewMatrix();
          // m_Engine_Ptr->SetViewMatrix(ViewMatrix_cPtr); // 保存 view matrix 給 class IRenderUnitEngine_OpenGL 取用

          CSmart_Ptr<Matrix4x4> ProjMatrix_cPtr(NULL);
          C3D_FLOAT fh, z_near, z_far;

          if(Camera_Ptr->GetPerspective(fh, z_near, z_far))
          {
            C3D_FLOAT aspect;
            aspect = Height;
            aspect /= Width;
            ProjMatrix_cPtr = MatrixPerspectiveRH_OpenGL(fh, aspect, z_near, z_far);
          }
          else
          {
            Camera_Ptr->GetOrtho(fh, z_near, z_far);
            ProjMatrix_cPtr = MatrixOrthoRH_OpenGL(fh * Width / Height, fh, z_near, z_far);
          }

          glMatrixMode(GL_PROJECTION);
          glLoadMatrixf((C3D_FLOAT*)(Matrix4x4*)ProjMatrix_cPtr); // Set projection matrix

          m_RDDC_Ptr->CallICameraLoopReset();

          Smart_Ptr<IRenderPipe> IRenderPipe_Ptr = Camera_Ptr->GetIRenderPipe();
          if(IRenderPipe_Ptr.isNULL() == false)
          {
            Smart_Ptr<cxxlList<IRenderDatagram> > IRenderDatagram_List = IRenderPipe_Ptr->GetIRenderDatagramList();
            if(IRenderDatagram_List.isNULL() == false)
            {            
              IRenderDatagram_List->ResetPT(toHead);
              for(Smart_Ptr<IRenderDatagram> IRenderDatagram_Ptr = (*IRenderDatagram_List)++;
               IRenderDatagram_Ptr.isNULL() == false;
               IRenderDatagram_Ptr = (*IRenderDatagram_List)++)
              {
                if(m_RDDC_Ptr->Exec(IRenderDatagram_Ptr,Camera_Ptr->GetViewMatrix()) == false)
                  return false;
              }
            }
          }
        }
      
        SwapBuffers(ActiveOpenGLContext_Ptr->Get_hDC());      // 交換緩存 (雙緩存)        
      }
      
      return true;
  }

  void cxxlFASTCALL SysInit(const Smart_Ptr<IWindow_Win32> &IWindow_Win32_Arg)
  {
    IWindow_Win32_Ptr = IWindow_Win32_Arg;
    m_RDDC_Ptr = new RenderDatagramDisposalCenter();
    m_OpenGLContext_Ptr = NewOpenGLContext(IWindow_Win32_Arg);
  }


protected:

  bool cxxlFASTCALL Ref(CxxlMan::Filter *F)
  {
    if(F != NULL && IRender_Win32_OpenGL::Ref(F))
      return true;
    else
      return false;
  }

public:
  // Constructor
  CRender_Win32_OpenGL()
    :cxxlObject(Spirit_Urgent),
    cxxlPreserve2(CRender_Win32_OpenGL_ClassID),
    IWindow_Win32_Ptr(NULL, this, L"CRender_Win32_OpenGL.IWindow_Win32"),
    m_RDDC_Ptr(NULL, this),
    m_OpenGLContext_Ptr(NULL, this)
  {
    //if(IWindow_Win32_Ptr.isNULL() == false)
      //IWindow_Win32_Ptr->Attach((IWindowWin32_Observer*)this);
    
    m_pInitOpenglFunc = NULL;

   // WinWidth = WinHigh = -1;
  }

  // Destructor
  virtual ~CRender_Win32_OpenGL()
  {
  }


  friend cxxlObject * cxxlCDECL CxxlMan_ObjectFactory(const wchar_t *ClassID_Arg, void *Arg, ISpirit *spirit);
};


extern "C" CXXL_DLLEXPORT
cxxlObject * cxxlCDECL CxxlMan_ObjectFactory(const wchar_t *ClassID_Arg, void *Arg, ISpirit *spirit)
{
  if(wcscmp(IRender_Win32_OpenGL_ClassID, ClassID_Arg) == 0)
    return new CRender_Win32_OpenGL();


  return NULL;
}

extern "C" CXXL_DLLEXPORT
cxxlPreserve2 * cxxlCDECL CxxlMan_Preserve2Factory(const wchar_t *ClassID_Arg)
{
  if(wcscmp(CRender_Win32_OpenGL_ClassID, ClassID_Arg) == 0)
    return new CRender_Win32_OpenGL();

  return NULL;
}


